home *** CD-ROM | disk | FTP | other *** search
- /* CD Shell Image conversion tool.
- * Copyright (C) 2002-2004 Michael K Ter Louw
- *
- * Version 1.1
- * - Can convert both directions: bmp->csi and csi->bmp.
- *
- * Version 1.0
- * - First release. Converts BMP files to CSI format.
- */
-
- #include <ctype.h>
- #include <stdio.h>
- #include <string.h>
-
- /* Image conversion modes. */
- #define BMP_2_CSI 1
- #define CSI_2_BMP 2
-
- /* Bitmap image format options. */
- #define BMP_WINDOWS 0
- #define BMP_OS2 1
-
- /* Bitmap file headers. */
- char bmp_file_header[] =
- { 'B', 'M', /* BMP signature */
- 0x36, 0x10, 0x0E, 0x00, /* File size */
- 0x00, 0x00, 0x00, 0x00, /* Reserved space */
- 0x36, 0x00, 0x00, 0x00 /* Image offset (for Windows format) */
- };
-
- char bmp_info_header[] =
- { 0x28, 0x00, 0x00, 0x00, /* Info header size (for Windows format) */
- 0x80, 0x02, 0x00, 0x00, /* Image width (640 pixels) */
- 0xE0, 0x01, 0x00, 0x00, /* Image height (480 pixels) */
- 0x01, 0x00, /* Number of planes */
- 0x18, 0x00 /* Color depth (24bpp) */
- };
-
- char bmp_extra_header[] =
- { 0x00, 0x00, 0x00, 0x00, /* Compression type (none) */
- 0x00, 0x10, 0x0E, 0x00, /* Image data size (921600 bytes) */
- 0x13, 0x0B, 0x00, 0x00, /* Horizontal pixels per meter */
- 0x13, 0x0B, 0x00, 0x00, /* Vertical pixels per meter */
- 0x00, 0x00, 0x00, 0x00, /* Number of colors used (i.e., palette size) */
- 0x00, 0x00, 0x00, 0x00 /* Number of important colors */
- };
-
- /* CSI Encoding flags. */
- #define FL_BANK_TRIGGER_32 0x8000
- #define FL_BANK_TRIGGER_24 0x4000
- #define FL_SPLIT_PIXEL 0x2000
- #define FL_ABSOLUTE_BLOCK 0x2000
- #define FL_64KB_FILE_BOUNDARY 0x2000
-
- /* CD Shell Image header. */
- const char * csi_header[] =
- { "-CSI",
- "CD Shell Image, v1.0",
- "http://www.cdshell.org"
- };
-
- /* Pixel boundaries that trigger a bank switch in 24bpp mode. */
- long bank_trigger_24[] =
- { 21845, 43690, 65536,
- 87381, 109226, 131072,
- 152917, 174762, 196608,
- 218453, 240298, 262144,
- 283989, 305834, 307200, /* Last value represents end of image. */
- -1
- };
-
- /* Pixel boundaries that trigger a bank switch in 32bpp mode. */
- long bank_trigger_32[] =
- { 16384, 32768, 49152, 65536,
- 81920, 98304, 114688, 131072,
- 147456, 163840, 180224, 196608,
- 212992, 229376, 245760, 262144,
- 278528, 294912,
- -1
- };
-
- /* Image data buffer. Intermediate storage between decoding and encoding operations. */
- unsigned char image_data[640 * 480 * 3];
-
- const char error_bmp_general[] = "Error reading input file, or unexpected end of file.\n";
- const char error_file_write[] = "Error writing to output file.\n";
-
- /* Function prototypes. */
- int process_command_line(int , char **, int *, char **, char **);
- int decode_bmp(char *);
- int encode_bmp(char *, int);
- int decode_csi(char *);
- int encode_csi(char *);
- int simple_strnicmp(const char *, const char *, int);
-
-
- /* Program entry procedure. */
- int main(int argc, char **argv)
- { int conversion = 0;
- char *infile = NULL;
- char *outfile = NULL;
-
- printf("\nCD Shell Image conversion utility, Version 1.1\n"
- "Copyright (C) 2002-2004 Michael K Ter Louw\n"
- "Visit http://www.cdshell.org for terms of use.\n\n");
-
- if (process_command_line(argc, argv, &conversion, &infile, &outfile))
- { printf("\nSyntax: convert <-bmp2csi | -csi2bmp> <-o outputfile> <inputfile>\n");
- return 1;
- }
-
- switch (conversion)
- { case BMP_2_CSI:
- if (decode_bmp(infile)) return 1;
- if (encode_csi(outfile)) return 1;
- break;
- case CSI_2_BMP:
- if (decode_csi(infile)) return 1;
- if (encode_bmp(outfile, BMP_WINDOWS)) return 1;
- }
- printf("Image encoded successfully.\n");
- return 0;
- }
-
-
-
-
- /* Process command line arguments. */
- int process_command_line(int argc, char *argv[], int *conversion, char **infile, char **outfile)
- { int h;
-
- for (h = 1; h < argc; h++)
- { /* Check for input file. */
- if (*(argv[h]) != '-')
- { if (*infile != NULL)
- { printf("Error: Input file already specified.\n");
- return 1;
- }
- *infile = argv[h]; continue;
- }
-
- /* Handle command switches. */
- if (!simple_strnicmp(argv[h], "-bmp2csi", 9))
- { if (*conversion)
- { printf("Conversion mode already specified.\n");
- return 1;
- }
- *conversion = BMP_2_CSI;
- continue;
- }
-
- if (!simple_strnicmp(argv[h], "-csi2bmp", 9))
- { if (*conversion)
- { printf("Conversion mode already specified.\n");
- return 1;
- }
- *conversion = CSI_2_BMP;
- continue;
- }
-
- if (!simple_strnicmp(argv[h], "-o", 3))
- { if (*outfile != NULL)
- { printf("Error: Output file already specified.\n");
- return 1;
- }
- if (h == argc - 1)
- { printf("Error: Output file expected.\n");
- return 1;
- }
- h++; *outfile = argv[h]; continue;
- }
-
- if (!simple_strnicmp(argv[h], "-v", 3))
- {
- }
-
- printf("Error: Unrecognized option: \"%s\"\n", argv[h]);
- return 1;
- }
-
- /* Check to see if parameters were not defined. */
- if (!(*conversion))
- { printf("Error: Conversion mode not specified.\n");
- return 1;
- }
- if (!(*infile))
- { printf("Error: Input file not specified.\n");
- return 1;
- }
- if (!(*outfile))
- { printf("Error: Output file not specified.\n");
- return 1;
- }
- return 0;
- }
-
-
-
-
- /* Decode a bitmap file. */
- int decode_bmp(char * filename)
- { long buffer_index, h;
- long image_offset = 14;
- unsigned short p[2];
- unsigned long q[2];
- unsigned char c, d, e[2];
- FILE * input_file;
-
- /* Read BMP file header. */
- if (!(input_file = fopen(filename, "rb")))
- { printf("Error opening input file: \"%s\"\n", filename);
- return 1;
- }
- /* Verify BMP signature. */
- if ((fread(e, 2, 1, input_file) < 1) || e[0] != bmp_file_header[0] ||
- e[1] != bmp_file_header[1])
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- /* Determine image offset (different depending on Windows or OS/2 format. */
- if (fseek(input_file, 14, SEEK_SET))
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- if (fread(q, 4, 1, input_file) < 1)
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- image_offset += q[0];
- /* Verify image width and height. */
- if (fread(q, 4, 2, input_file) < 2)
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- if ((q[0] != 640) || (q[1] != 480))
- { printf("Error: BMP file is not 640 by 480.\n"); fclose(input_file); return 1;
- }
- /* Verify color depth. */
- if (fread(p, 2, 2, input_file) < 2)
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- if (p[1] != 24)
- { printf("Error: BMP file is not 24 bits per pixel.\n"); fclose(input_file); return 1;
- }
- /* Read the image data. */
- if (fseek(input_file, image_offset, SEEK_SET))
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- printf("Bitmap image verified 640x480x24.\n");
- buffer_index = 640 * 479 * 3;
- for (h = 0; h < 480; buffer_index -= 640 * 3, h++)
- { if ((fread(image_data + buffer_index, 640 * 3, 1, input_file)) != 1)
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- }
- fclose(input_file);
- return 0;
- }
-
-
-
-
- /* Create a BMP image file. */
- int encode_bmp(char * filename, int format)
- { int buffer_index;
- int h, i;
- FILE * output_file;
-
- /* Open the output file. */
- if (!(output_file = fopen(filename, "wb")))
- { printf("Couldn't open output file \"%s\".\n", filename);
- return 1;
- }
-
- printf("Encoding Bitmap (BMP) Image...\n");
-
- /* Generate header. */
- if (format)
- { /* Set header values to OS/2 format. */
- bmp_file_header[10] = 26;
- bmp_info_header[0] = 12;
- }
- fwrite(bmp_file_header, 14, 1, output_file);
- fwrite(bmp_info_header, 16, 1, output_file);
- fwrite(bmp_extra_header, 24, 1, output_file);
-
- for (h = 0; h < 480; h++)
- { buffer_index = 640 * 3 * (479 - h);
- for (i = 0; i < 640 * 3; buffer_index++, i++)
- fputc(image_data[buffer_index], output_file);
- }
- fclose(output_file);
- return 0;
-
- }
-
-
-
- /* Decode a CD Shell Image (Version 1) file. */
- int decode_csi(char * filename)
- { long buffer_index, block_code, file_pos = 0x40;
- unsigned char c[6];
- FILE * input_file;
-
- /* Read CSI file header. */
- if (!(input_file = fopen(filename, "rb")))
- { printf("Error opening input file: \"%s\"\n", filename);
- return 1;
- }
- /* Verify CSI signature. */
- if ((fread(c, 4, 1, input_file) < 1) || (c[0] != '-') ||
- (c[1] != 'C') || (c[2] != 'S') || (c[3] != 'I'))
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- /* Verify CSI version. */
- if ((fread(c, 2, 1, input_file) < 1) || (c[0] != 1) || (c[1] != 0))
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- printf("CD Shell Image format verified 640x480x24.\n");
- /* Read the rest of the header. */
- if ((fread(image_data, 0x40 - 6, 1, input_file) < 1))
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
-
- /* Decode encoded blocks. */
- for (buffer_index = 0; 1; )
- { /* Read the block code. */
- if ((fread(c, 2, 1, input_file) < 1))
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- block_code = (long) (c[1] << 8) + c[0];
- file_pos += 2;
-
- /* Check for end of file reached. */
- if (!block_code)
- { if (buffer_index != 640 * 480 * 3)
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- return 0;
- }
-
- /* Handle file-block position markers. */
- if (block_code == FL_64KB_FILE_BOUNDARY)
- { if ((file_pos % 65536) && (fread(c, 65536 - (file_pos % 65536), 1, input_file) < 1))
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- file_pos = (file_pos + 65535) & ~65535;
- continue;
- }
-
- /* Handle 24bpp bank triggers. */
- if (block_code & FL_BANK_TRIGGER_24)
- { if (block_code & FL_SPLIT_PIXEL)
- { if ((fread(image_data + buffer_index, 3, 1, input_file) < 1))
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- buffer_index += 3; file_pos += 3;
- }
- continue;
- }
-
- /* Ignore 32bpp bank triggers. */
- else if (block_code & FL_BANK_TRIGGER_32) continue;
-
- /* Handle absolute blocks. */
- if (block_code & FL_ABSOLUTE_BLOCK)
- { block_code ^= FL_ABSOLUTE_BLOCK;
- file_pos += block_code * 3;
- for (; block_code; block_code--)
- { if ((fread(image_data + buffer_index, 3, 1, input_file) < 1))
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- buffer_index += 3;
- }
- continue;
- }
-
- /* Handle repeat blocks. */
- else
- { if ((fread(c, 3, 1, input_file) < 1))
- { printf(error_bmp_general); fclose(input_file); return 1;
- }
- for (; block_code; block_code--)
- { image_data[buffer_index + 0] = c[0];
- image_data[buffer_index + 1] = c[1];
- image_data[buffer_index + 2] = c[2];
- buffer_index += 3;
- }
- file_pos += 3;
- continue;
- }
- }
- fclose(input_file);
- return 0;
- }
-
-
-
-
- /* Create a CD Shell Image file (Version 1 format). */
- int encode_csi(char * filename)
- { int h, i;
- int file_block_pos;
- unsigned char r, g, b;
- FILE * output_file;
-
- long current_pixel = 0;
- long file_pos;
- long block_code;
- long repeat_count;
- long absolute_count;
-
-
- if (!(output_file = fopen(filename, "wb")))
- { printf("Couldn't open output file \"%s\".\n", filename);
- return 1;
- }
-
- printf("Encoding CD Shell Image (version 1)...\n");
-
- /* Generate header. */
- fwrite(csi_header[0], 4, 1, output_file);
- block_code = 1; /* block_code = CD Shell Image version. */
- fwrite(&block_code, 2, 1, output_file);
- fwrite(csi_header[1], strlen(csi_header[1]) + 1, 1, output_file);
- fwrite(csi_header[2], strlen(csi_header[2]) + 1, 1, output_file);
- h = (int) (0x40 - (strlen(csi_header[1]) + strlen(csi_header[2]) + 8));
- for (; h > 0; h--) fputc('\0', output_file);
- file_pos = 0x40;
-
- /* Generate encoded blocks. */
- for (block_code = 0; 1; block_code = 0)
- { /* Terminate if end of image has been reached. */
- if (current_pixel == 640 * 480) break;
-
- /* Check for 64kb file boundary. */
- file_block_pos = file_pos % 65536;
- if (file_block_pos > (65536 - 7))
- { block_code = FL_64KB_FILE_BOUNDARY;
- fwrite(&block_code, 2, 1, output_file); file_block_pos += 2;
- for (; file_block_pos < 65536; file_block_pos++)
- fputc('\0', output_file);
- file_pos = (file_pos + 65535) & ~65535;
- continue;
- }
-
- /* Check to see if the pixel lies on a bank boundary. */
- for(i = 0; bank_trigger_24[i] != -1; i++)
- { if (bank_trigger_24[i] == current_pixel)
- { block_code |= FL_BANK_TRIGGER_24;
- bank_trigger_24[i] = 0;
- break;
- }
- }
- for(i = 0; bank_trigger_32[i] != -1; i++)
- { if (bank_trigger_32[i] == current_pixel)
- { block_code |= FL_BANK_TRIGGER_32;
- bank_trigger_32[i] = 0;
- break;
- }
- }
-
- /* Generate bank switch trigger block. */
- if (block_code)
- { /* Check for pixel split across banks (only in 24bpp mode). */
- if (current_pixel & 16383)
- { block_code |= FL_SPLIT_PIXEL;
- fwrite(&block_code, 2, 1, output_file);
- b = image_data[current_pixel * 3 + 0];
- g = image_data[current_pixel * 3 + 1];
- r = image_data[current_pixel * 3 + 2];
- fwrite(&b, 1, 1, output_file);
- fwrite(&g, 1, 1, output_file);
- fwrite(&r, 1, 1, output_file);
- current_pixel++; file_pos += 5;
- }
- /* No split pixel, just a nice clean bank switch. */
- else
- { fwrite(&block_code, 2, 1, output_file);
- file_pos += 2;
- }
- continue;
- }
-
- /* Check for repeated data. */
- for (repeat_count = 1, i = current_pixel * 3; repeat_count < 8191; repeat_count++, i += 3)
- { /* Break loop if end of run found. */
- if ((image_data[i + 0] != image_data[i + 3]) ||
- (image_data[i + 1] != image_data[i + 4]) ||
- (image_data[i + 2] != image_data[i + 5])) break;
- /* Don't let repeat runs span across bank boundaries. */
- for(h = 0;
- (bank_trigger_24[h] != -1) &&
- (bank_trigger_24[h] != current_pixel + repeat_count)
- ; h++);
- if (bank_trigger_24[h] == current_pixel + repeat_count) break;
- for(h = 0;
- (bank_trigger_32[h] != -1) &&
- (bank_trigger_32[h] != current_pixel + repeat_count)
- ; h++);
- if (bank_trigger_32[h] == current_pixel + repeat_count) break;
- }
-
- /* Generate repeat block if repeated data was found. */
- if (repeat_count > 1)
- { fwrite(&repeat_count, 2, 1, output_file);
- b = image_data[current_pixel * 3 + 0];
- g = image_data[current_pixel * 3 + 1];
- r = image_data[current_pixel * 3 + 2];
- fwrite(&b, 1, 1, output_file);
- fwrite(&g, 1, 1, output_file);
- fwrite(&r, 1, 1, output_file);
- current_pixel += repeat_count; file_pos += 5;
- continue;
- }
-
- /* No repeated data, so put together an absolute block. */
- for (absolute_count = 1, i = current_pixel * 3; absolute_count < 8191; absolute_count++, i += 3)
- { /* Don't let absolute block span across 64kb file-block boundary. */
- if ((file_block_pos + 2 + absolute_count * 3) > 65531) break;
- /* Don't let absolute block span across bank boundaries. */
- for(h = 0;
- (bank_trigger_24[h] != -1) &&
- (bank_trigger_24[h] != current_pixel + absolute_count)
- ; h++);
- if (bank_trigger_24[h] == current_pixel + absolute_count) break;
- for(h = 0;
- (bank_trigger_32[h] != -1) &&
- (bank_trigger_32[h] != current_pixel + absolute_count)
- ; h++);
- if (bank_trigger_32[h] == current_pixel + absolute_count) break;
- /* End absolute block if repeated data is found. */
- if ((image_data[i + 0] != image_data[i + 3]) ||
- (image_data[i + 1] != image_data[i + 4]) ||
- (image_data[i + 2] != image_data[i + 5])) continue;
- if ((image_data[i + 3] != image_data[i + 6]) ||
- (image_data[i + 4] != image_data[i + 7]) ||
- (image_data[i + 5] != image_data[i + 8])) continue;
- break;
- }
-
- /* Output the absolute block to the file. */
- block_code = absolute_count | FL_ABSOLUTE_BLOCK;
- fwrite(&block_code, 2, 1, output_file);
- for (h = 0, i = current_pixel * 3; h < absolute_count; h++, i += 3)
- { b = image_data[i + 0];
- g = image_data[i + 1];
- r = image_data[i + 2];
- fwrite(&b, 1, 1, output_file);
- fwrite(&g, 1, 1, output_file);
- fwrite(&r, 1, 1, output_file);
- }
- current_pixel += absolute_count;
- file_pos += (absolute_count * 3) + 2;
- }
-
- /* Write end of file-block code. */
- block_code = 0;
- fwrite(&block_code, 2, 1, output_file);
-
- /* Done encoding the file. */
- fclose(output_file);
- return 0;
- }
-
-
-
-
- /* Included because strnicmp is not ANSI. */
- int simple_strnicmp(const char source[], const char dest[], int max)
- { int h;
-
- for (h = 0; h < max; h++)
- { if ((tolower(source[h]) != tolower(dest[h])) ||
- ((!source[h] || !dest[h]) && (h != (max - 1))))
- return 1;
- }
- return 0;
- }